﻿Imports System.Runtime.InteropServices


Namespace uHook

    ''' <summary>
    ''' hook的抽象类
    ''' </summary>
    ''' <remarks></remarks>
    Public MustInherit Class BaseHook

#Region "API声明"
        ''' <summary>
        ''' HookProc 钩子处理过程
        ''' </summary>
        ''' <param name="code"></param>
        ''' <param name="wParam"></param>
        ''' <param name="lParam"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        <UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)> _
        Protected Delegate Function HookProc(ByVal code As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer

        Protected Enum HookType As Integer
            ''' <summary>
            '''	安装钩子程序，在系统将消息发送到目标窗口过程之前监视消息。有关更多信息，请参阅CallWndProc挂钩过程。
            ''' </summary>
            WH_CALLWNDPROC = 4
            ''' <summary>
            '''	安装钩子程序，在目标窗口过程处理消息后监视消息。有关更多信息，请参阅CallWndRetProc挂钩过程。
            ''' </summary>
            WH_CALLWNDPROCRET = 12
            ''' <summary>
            '''	安装钩子程序，接收对基于计算机的培训（CBT）应用程序有用的通知。有关更多信息，请参阅CBTProc挂钩过程。
            ''' </summary>
            WH_CBT = 5
            ''' <summary>
            '''	安装可用于调试其他挂钩程序的挂钩程序。有关更多信息，请参阅DebugProc挂钩过程。
            ''' </summary>
            WH_DEBUG = 9
            ''' <summary>
            '''	安装将在应用程序的前台线程即将变为空闲时调用的钩子过程。该挂钩对于在空闲时执行低优先级任务很有用。有关更多信息，请参阅ForegroundIdleProc挂钩过程。
            ''' </summary>
            WH_FOREGROUNDIDLE = 11
            ''' <summary>
            '''	安装一个监视发送到消息队列的消息的挂钩过程。有关更多信息，请参阅GetMsgProc挂钩过程。
            ''' </summary>
            WH_GETMESSAGE = 3
            ''' <summary>
            '''	安装一个挂钩过程，用于发布先前由WH_JOURNALRECORD挂钩过程记录的消息。有关更多信息，请参阅JournalPlaybackProc挂钩过程。
            ''' </summary>
            WH_JOURNALPLAYBACK = 1
            ''' <summary>
            '''	安装一个挂钩过程，记录发布到系统消息队列的输入消息。这个钩子对于录制宏很有用。有关更多信息，请参阅JournalRecordProc挂钩过程。
            ''' </summary>
            WH_JOURNALRECORD = 0
            ''' <summary>
            '''	安装监视按键消息的挂钩过程。有关更多信息，请参阅KeyboardProc挂钩过程。
            ''' </summary>
            WH_KEYBOARD = 2
            ''' <summary>
            '''	Windows NT / 2000 / XP：安装监视低级键盘输入事件的挂钩程序。有关更多信息，请参阅LowLevelKeyboardProc挂钩过程。
            ''' </summary>
            WH_KEYBOARD_LL = 13
            ''' <summary>
            '''	安装监视鼠标消息的挂钩过程。有关更多信息，请参阅MouseProc挂钩过程。
            ''' </summary>
            WH_MOUSE = 7
            ''' <summary>
            '''	Windows NT / 2000 / XP：安装监视低级别鼠标输入事件的挂钩程序。有关更多信息，请参阅LowLevelMouseProc挂钩过程。
            ''' </summary>
            WH_MOUSE_LL = 14
            ''' <summary>
            '''	安装钩子程序，用于监视在对话框，消息框，菜单或滚动条中由于输入事件而生成的消息。有关更多信息，请参阅MessageProc挂钩过程。
            ''' </summary>
            WH_MSGFILTER = -1
            ''' <summary>
            '''	安装接收对shell应用程序有用的通知的挂钩程序。有关更多信息，请参阅ShellProc挂钩过程。
            ''' </summary>
            WH_SHELL = 10
            ''' <summary>
            '''	安装钩子程序，用于监视在对话框，消息框，菜单或滚动条中由于输入事件而生成的消息。钩子程序监视与调用线程相同的桌面中的所有应用程序的这些消息。有关更多信息，请参阅SysMsgProc挂钩过程。
            ''' </summary>
            WH_SYSMSGFILTER = 6
        End Enum


        ''' <summary>
        '''		SetWindowsHookEx函数将应用程序定义的挂钩过程安装到挂钩链中。
        '''		您可以安装挂钩程序来监视系统是否存在某些类型的事件。
        '''		这些事件与特定线程或调用线程所在的桌面中的所有线程相关联。
        ''' </summary>
        ''' <param name="idHook">
        '''		[in]指定要安装的挂钩过程的类型。
        '''		该参数可以是以下值之一。
        '''		<list type="table">
        '''			<para>WH_CALLWNDPROC              安装钩子程序，在系统将消息发送到目标窗口过程之前监视消息。有关更多信息，请参阅CallWndProc挂钩过程。 </para>
        '''			<para>WH_CALLWNDPROCRET           安装钩子程序，在目标窗口过程处理消息后监视消息。有关更多信息，请参阅CallWndRetProc挂钩过程。 </para>
        '''			<para>WH_CBT                      安装钩子程序，接收对基于计算机的培训（CBT）应用程序有用的通知。有关更多信息，请参阅CBTProc挂钩过程。 </para>
        '''			<para>WH_DEBUG                    安装可用于调试其他挂钩程序的挂钩程序。有关更多信息，请参阅DebugProc挂钩过程。 </para>
        '''			<para>WH_FOREGROUNDIDLE           安装将在应用程序的前台线程即将变为空闲时调用的钩子过程。该挂钩对于在空闲时执行低优先级任务很有用。有关更多信息，请参阅ForegroundIdleProc挂钩过程。 </para>
        '''			<para>WH_GETMESSAGE               安装一个监视发送到消息队列的消息的挂钩过程。有关更多信息，请参阅GetMsgProc挂钩过程。 </para>
        '''			<para>WH_JOURNALPLAYBACK          安装一个挂钩过程，用于发布先前由WH_JOURNALRECORD挂钩过程记录的消息。有关更多信息，请参阅JournalPlaybackProc挂钩过程。 </para>
        '''			<para>WH_JOURNALRECORD            安装一个挂钩过程，记录发布到系统消息队列的输入消息。这个钩子对于录制宏很有用。有关更多信息，请参阅JournalRecordProc挂钩过程。 </para>
        '''			<para>WH_KEYBOARD                 安装监视按键消息的挂钩过程。有关更多信息，请参阅KeyboardProc挂钩过程。 </para>
        '''			<para>WH_KEYBOARD_LL              Windows NT / 2000 / XP：安装监视低级键盘输入事件的挂钩程序。有关更多信息，请参阅LowLevelKeyboardProc挂钩过程。 </para>
        '''			<para>WH_MOUSE                    安装监视鼠标消息的挂钩过程。有关更多信息，请参阅MouseProc挂钩过程。 </para>
        '''			<para>WH_MOUSE_LL                 Windows NT / 2000 / XP：安装监视低级别鼠标输入事件的挂钩程序。有关更多信息，请参阅LowLevelMouseProc挂钩过程。 </para>
        '''			<para>WH_MSGFILTER                安装钩子程序，用于监视在对话框，消息框，菜单或滚动条中由于输入事件而生成的消息。有关更多信息，请参阅MessageProc挂钩过程。 </para>
        '''			<para>WH_SHELL                    安装接收对shell应用程序有用的通知的挂钩程序。有关更多信息，请参阅ShellProc挂钩过程。 </para>
        '''			<para>WH_SYSMSGFILTER             安装钩子程序，用于监视在对话框，消息框，菜单或滚动条中由于输入事件而生成的消息。钩子程序监视与调用线程相同的桌面中的所有应用程序的这些消息。有关更多信息，请参阅SysMsgProc挂钩过程。 </para>
        '''		</list>
        ''' </param>
        ''' <param name="lpfn">
        '''		[in]指向挂钩程序的指针。
        '''		如果dwThreadId参数为零或指定由不同进程创建的线程的标识符，则lpfn参数必须指向DLL中的钩子过程。
        '''		否则，lpfn可以指向与当前进程关联的代码中的钩子过程。
        ''' </param>
        ''' <param name="hMod">
        '''		[in]处理包含由lpfn参数指向的挂钩过程的DLL。
        '''		如果dwThreadId参数指定由当前进程创建的线程，并且钩子过程位于与当前进程关联的代码中，则hMod参数必须设置为NULL。
        ''' </param>
        ''' <param name="dwThreadId">
        '''		[in]指定与钩子程序关联的线程的标识符。
        '''		如果此参数为零，则挂钩过程与在调用线程所在的同一桌面上运行的所有现有线程相关联。
        ''' </param>
        ''' <returns>
        '''		如果函数成功，则返回值是钩子过程的句柄。
        '''		如果函数失败，返回值为NULL。
        '''		要获得扩展的错误信息，请调用GetLastError。
        ''' </returns>
        <DllImport("user32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall, SetLastError:=True)>
        Protected Shared Function SetWindowsHookEx(idHook As Integer, lpfn As HookProc, hMod As IntPtr, dwThreadId As Integer) As Integer
        End Function

        ''' <summary>
        '''		检索指定模块的模块句柄。
        '''		该模块必须已由调用进程加载。
        ''' </summary>
        ''' <param name="lpModuleName">
        '''		加载的模块的名称（.dll或.exe文件）。
        '''		如果省略文件扩展名，则会附加默认库扩展名.dll。
        '''		文件名字符串可以包含尾随点字符（。
        '''		），以指示模块名称没有扩展名。
        '''		该字符串不必指定路径。
        '''		指定路径时，请确保使用反斜杠（\\），而不是正斜杠（/）。
        '''		该名称与当前映射到调用进程的地址空间中的模块的名称（独立情况下）进行比较。
        '''		如果此参数为NULL，则GetModuleHandle将返回用于创建调用进程（.exe文件）的文件的句柄。
        '''		GetModuleHandle函数不会检索使用LOAD_LIBRARY_AS_DATAFILE标志加载的模块的句柄。
        '''		有关更多信息，请参阅LoadLibraryEx。
        ''' </param>
        ''' <returns>
        '''		如果函数成功，则返回值是指定模块的句柄。
        '''		如果函数失败，则返回值为NULL。
        '''		要获得扩展的错误信息，请调用GetLastError。
        ''' </returns>
        <DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
        Protected Shared Function GetModuleHandle(lpModuleName As [String]) As IntPtr
        End Function

        ''' <summary>
        '''		UnhookWindowsHookEx函数通过SetWindowsHookEx函数删除安装在钩子链中的钩子程序。
        ''' </summary>
        ''' <param name="hhk">
        '''		[in]处理钩子以被移除。
        '''		此参数是通过以前调用SetWindowsHookEx获取的钩子句柄。
        ''' </param>
        ''' <returns>
        '''		如果函数成功，则返回值不为零。
        '''		如果函数失败，返回值为零。
        '''		要获得扩展的错误信息，请调用GetLastError。
        ''' </returns>
        <DllImport("user32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall, SetLastError:=True)>
        Protected Shared Function UnhookWindowsHookEx(hhk As Integer) As Integer
        End Function

        ''' <summary>
        '''		CallNextHookEx函数将钩子信息传递给当前钩子链中的下一个钩子程序。
        '''		钩子程序可以在处理钩子信息之前或之后调用该函数。
        ''' </summary>
        ''' <param name="hhk">
        '''		[在] Windows 95/98 / ME：处理当前钩子。
        '''		由于之前调用SetWindowsHookEx函数，应用程序接收到此句柄。
        '''		Windows NT / XP / 2003：忽略。
        ''' </param>
        ''' <param name="nCode">
        '''		[in]指定传递给当前钩子程序的钩子代码。
        '''		下一个挂钩程序使用此代码来确定如何处理挂钩信息。
        ''' </param>
        ''' <param name="wParam">
        '''		[in]指定传递给当前钩子程序的wParam值。
        '''		此参数的含义取决于与当前钩子链相关的钩子的类型。
        ''' </param>
        ''' <param name="lParam">
        '''		[in]指定传递给当前钩子程序的lParam值。
        '''		此参数的含义取决于与当前钩子链相关的钩子的类型。
        ''' </param>
        ''' <returns>
        '''		该值由链中的下一个挂钩过程返回。
        '''		当前钩子程序也必须返回这个值。
        '''		返回值的含义取决于钩子类型。
        '''		有关更多信息，请参阅各个挂钩程序的说明。
        ''' </returns>
        <DllImport("user32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)>
        Protected Shared Function CallNextHookEx(hhk As Integer, nCode As Integer, wParam As Integer, lParam As IntPtr) As Integer
        End Function
        Public Enum Return_Enum

            ''' <summary>
            '''	含义
            ''' </summary>
            Value = &H19
        End Enum

        ''' <summary>
        '''		ToAscii函数将指定的虚拟键代码和键盘状态转换为相应的一个或多个字符。
        '''		该功能使用由键盘布局手柄识别的输入语言和物理键盘布局翻译代码。
        ''' </summary>
        ''' <param name="uVirtKey">
        '''		[in]指定要翻译的虚拟键码。
        ''' </param>
        ''' <param name="uScanCode">
        '''		[in]指定要翻译的密钥的硬件扫描码。
        '''		如果该键已启动（未按下），则此值的高位被设置。
        ''' </param>
        ''' <param name="lpKeyState">
        '''		[in]指向包含当前键盘状态的256字节数组的指针。
        '''		数组中的每个元素（字节）都包含一个键的状态。
        '''		如果设置了一个字节的高位，则按下（按下）键。
        '''		低位（如果置位）表示该键已打开。
        '''		在此功能中，只有CAPS LOCK键的切换位是相关的。
        '''		NUM LOCK和SCROLL LOCK键的切换状态将被忽略。
        ''' </param>
        ''' <param name="lpChar">
        '''		[out]指向接收翻译后的字符或字符的缓冲区的指针。
        ''' </param>
        ''' <param name="uFlags">
        '''		[in]指定菜单是否处于活动状态。
        '''		如果菜单处于活动状态，则此参数必须为1，否则为0。
        ''' </param>
        ''' <returns>
        '''		如果指定的键是死键，则返回值为负值。
        '''		否则，它是以下值之一。
        '''		<list type="table">
        '''			<para>Value                       含义 </para>
        '''			<para>0                           指定的虚拟键没有翻译键盘的当前状态。 </para>
        '''			<para>1                           一个字符被复制到缓冲区。 </para>
        '''			<para>2                           两个字符被复制到缓冲区。当键盘布局中存储的死锁字符（重音符号或变音符号）不能与指定的虚拟键组合以形成单个字符时，通常会发生这种情况。 </para>
        '''		</list>
        ''' </returns>
        <DllImport("user32")>
        Protected Shared Function ToAscii(uVirtKey As Integer, uScanCode As Integer, lpKeyState As Byte(), lpChar As Byte(), uFlags As Integer) As Integer
        End Function
        ''' <summary>
        '''		GetKeyboardState函数将256个虚拟键的状态复制到指定的缓冲区。
        ''' </summary>
        ''' <param name="lpKeyState">
        '''		[in]指向接收每个虚拟键的状态数据的256字节数组的指针。
        ''' </param>
        ''' <returns>
        '''		如果函数成功，则返回值不为零。
        '''		如果函数失败，返回值为零。
        '''		要获得扩展的错误信息，请调用GetLastError。
        ''' </returns>
        <DllImport("user32")>
        Protected Shared Function GetKeyboardState(lpKeyState As Byte()) As Integer
        End Function
        ''' <summary>
        '''		GetKeyState函数检索指定的虚拟键的状态。
        '''		状态指定键是上，下或切换（每按一次键，交替开或关）。
        ''' </summary>
        ''' <param name="nVirtKey">
        '''		[in]指定一个虚拟键。
        '''		如果所需的虚拟键是字母或数字（A到Z，a到z或0到9），则必须将nVirtKey设置为该字符的ASCII值。
        '''		对于其他键，它必须是虚拟键码。
        '''		如果使用非英文键盘布局，则使用ASCII A到Z和0到9范围内的值的虚拟键指定大部分字符键。
        '''		例如，对于德语键盘布局，值为ASCII O（0x4F）的虚拟键指的是“o”键，而VK_OEM_1指的是“o with umlaut”键。
        ''' </param>
        ''' <returns>
        '''		返回值指定了指定虚拟键的状态，如下所示：如果高位为1，键将关闭;否则，它是up的。
        '''		如果低位是1，则键被切换。
        '''		如果CAPS LOCK键处于打开状态，则该键会被切换。
        '''		如果低位为0，则该键处于关闭状态并且未被锁定。
        '''		当键被切换时，键盘上的切换键指示灯（如果有的话）会亮起，而当键未被切换时则会熄灭。
        ''' </returns>
        <DllImport("user32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)>
        Protected Shared Function GetKeyState(nVirtKey As Integer) As Short
        End Function

        ''' <summary>
        '''		检索调用线程的线程标识符。
        ''' </summary>
        ''' <returns>
        '''		返回值是调用线程的线程标识符。
        ''' </returns>
        <DllImport("kernel32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _
        Public Overloads Shared Function GetCurrentThreadId() As Integer
        End Function

#End Region

#Region "定义常量"
        Protected Const WM_MOUSEMOVE As Integer = &H200
        Protected Const WM_LBUTTONDOWN As Integer = &H201
        Protected Const WM_LBUTTONUP As Integer = &H202
        Protected Const WM_LBUTTONDBLCLK As Integer = &H203

        Protected Const WM_RBUTTONDOWN As Integer = &H204
        Protected Const WM_RBUTTONUP As Integer = &H205
        Protected Const WM_RBUTTONDBLCLK As Integer = &H206

        Protected Const WM_MBUTTONDOWN As Integer = &H207
        Protected Const WM_MBUTTONUP As Integer = &H208
        Protected Const WM_MBUTTONDBLCLK As Integer = &H209
        Protected Const WM_MOUSEWHEEL As Integer = &H20A

        Protected Const WM_KEYDOWN As Integer = &H100
        Protected Const WM_KEYUP As Integer = &H101
        Protected Const WM_SYSKEYDOWN As Integer = &H104
        Protected Const WM_SYSKEYUP As Integer = &H105

        Protected Const WM_NCMOUSEMOVE = &HA0
        Protected Const WM_NCLBUTTONDOWN = &HA1
        Protected Const WM_NCLBUTTONUP = &HA2
        Protected Const WM_NCLBUTTONDBLCLK = &HA3
        Protected Const WM_NCRBUTTONDOWN = &HA4
        Protected Const WM_NCRBUTTONUP = &HA5
        Protected Const WM_NCRBUTTONDBLCLK = &HA6
        Protected Const WM_NCMBUTTONDOWN = &HA7
        Protected Const WM_NCMBUTTONUP = &HA8
        Protected Const WM_NCMBUTTONDBLCLK = &HA9
        Protected Const WM_NCXBUTTONDOWN = &HAB
        Protected Const WM_NCXBUTTONUP = &HAC
        Protected Const WM_NCXBUTTONDBLCLK = &HAD

        Protected Const HC_ACTION = 0
        Protected Const HC_NOREMOVE = 3
        ''' <summary>
        ''' SHIFT键
        ''' </summary>
        Protected Const VK_SHIFT As Byte = &H10
        ''' <summary>
        '''大小写键 CAPS LOCK 键
        ''' </summary>
        Protected Const VK_CAPITAL As Byte = &H14
        ''' <summary>
        ''' NUM LOCK 键
        ''' </summary>
        Protected Const VK_NUMLOCK As Byte = &H90

        Protected Const VK_LSHIFT As Byte = &HA0
        Protected Const VK_RSHIFT As Byte = &HA1
        ''' <summary>
        ''' Left CTRL
        ''' </summary>
        Protected Const VK_LCONTROL As Byte = &HA2
        ''' <summary>
        ''' Right CTRL
        ''' </summary>
        Protected Const VK_RCONTROL As Byte = &H3
        ''' <summary>
        ''' Left ALT
        ''' </summary>
        Protected Const VK_LALT As Byte = &HA4
        ''' <summary>
        ''' Right ALT
        ''' </summary>
        Protected Const VK_RALT As Byte = &HA5
#End Region

#Region "结构"
        Public Structure CWPSTRUCT
            Public lparam As IntPtr
            Public wparam As IntPtr
            Public message As Integer
            Public hwnd As IntPtr
        End Structure

        '点
        <StructLayout(LayoutKind.Sequential)> _
        Public Class POINT
            Public x As Integer
            Public y As Integer
        End Class

        '鼠标消息结构
        <StructLayout(LayoutKind.Sequential)> _
        Public Class MouseHookStruct
            Public pt As Point
            Public hWnd As Integer
            Public wHitTestCode As Integer
            Public dwExtraInfo As Integer
        End Class

        <StructLayout(LayoutKind.Sequential)> _
        Public Class MouseLLHookStruct
            Public pt As Point
            Public mouseData As Integer
            Public flags As Integer
            Public time As Integer
            Public dwExtraInfo As Integer
        End Class

        '键盘消息结构
        <StructLayout(LayoutKind.Sequential)> _
        Public Class KeyboardHookStruct
            Public vkCode As Integer        '1到254间的虚似键盘码
            Public scanCode As Integer      '扫描码
            Public flags As Integer
            Public time As Integer
            Public dwExtraInfo As Integer
        End Class

#End Region

#Region "方法-Methods"
        ''' <summary>
        ''' 钩子回调函数,定义可以重写。
        ''' </summary>
        ''' <param name="nCode"></param>
        ''' <param name="wParam"></param>
        ''' <param name="lParam"></param>
        ''' <returns></returns>
        Protected Overridable Function HookCallbackProcedure(nCode As Integer, wParam As Int32, lParam As IntPtr) As Integer

            ' This method must be overriden by each extending hook  
            Return 0

        End Function
#End Region

    End Class


    <ComVisibleAttribute(False), Security.SuppressUnmanagedCodeSecurityAttribute()> _
    Public Class KeyboardHook
        Inherits BaseHook

        Public Event KeyDown As KeyEventHandler
        Public Event KeyPress As KeyPressEventHandler
        Public Event KeyUp As KeyEventHandler

        Private hKeyboardHook As Integer = 0
        Private KeyboardHookProcedure As HookProc

        Private ThreadHook As Boolean = True


        Public Sub New()
            Call Start()
        End Sub

        Public Sub New(ByVal mThreadHook As Boolean)
            ThreadHook = mThreadHook
            Call Start()
        End Sub

        Protected Overrides Sub Finalize()
            Try
                [Stop]()
            Finally
                MyBase.Finalize()
            End Try
        End Sub

        Private Sub Start()
            If (hKeyboardHook = 0) Then
                KeyboardHookProcedure = New HookProc(AddressOf KeyboardHookProc)
                If ThreadHook Then  '线程钩子
                    hKeyboardHook = SetWindowsHookEx(HookType.WH_KEYBOARD, _
                                                    KeyboardHookProcedure, _
                                                    GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), _
                                                    GetCurrentThreadId)
                Else '系统钩子()
                    hKeyboardHook = SetWindowsHookEx(HookType.WH_KEYBOARD_LL, _
                                                     KeyboardHookProcedure, _
                                                     GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), _
                                                     0)
                End If

                If (hKeyboardHook = 0) Then
                    [Stop]()
                    Throw New Exception("SetWindowsHookEx is failed in KeyboardHook.")
                End If
            End If
        End Sub

        Public Sub [Stop]()
            Dim retKeyboard As Boolean = True
            If hKeyboardHook <> 0 Then
                retKeyboard = UnhookWindowsHookEx(hKeyboardHook)
                hKeyboardHook = 0
            End If

            If Not retKeyboard Then Throw New Exception("UnhookWindowsHookEx failed.")
        End Sub

        Private Function KeyboardHookProc(ByVal nCode As Integer, ByVal wParam As Integer, ByVal lParam As IntPtr) As Integer
            If nCode >= 0 Then
                If ThreadHook Then
                    '我们从右向左数，假设最右边那位为第0位，最左边的就是第31位，那么该参数的的0-15位表示键的发送次数等扩展信息，
                    '16-23位为按键的扫描码，24-31位表示是按下键还是释放键。
                    If lParam.ToInt32 > 0 Then
                        RaiseEvent KeyDown(Me, New KeyEventArgs(CType(wParam, Keys)))
                    Else
                        RaiseEvent KeyUp(Me, New KeyEventArgs(CType(wParam, Keys)))
                    End If
                Else
                    Dim MyKeyboardHookStruct As KeyboardHookStruct
                    MyKeyboardHookStruct = CType(Marshal.PtrToStructure(lParam, GetType(KeyboardHookStruct)), KeyboardHookStruct)

                    If wParam = WM_KEYDOWN OrElse wParam = WM_SYSKEYDOWN Then
                        Dim keyData As Keys = CType(MyKeyboardHookStruct.vkCode, Keys)
                        Dim e As New KeyEventArgs(keyData)
                        RaiseEvent KeyDown(Me, e)
                    End If

                    If wParam = WM_KEYDOWN Then
                        Dim keyState As Byte() = New Byte(256 - 1) {}
                        Call GetKeyboardState(keyState)
                        Dim inBuffer As Byte() = New Byte(2 - 1) {}
                        If ToAscii(MyKeyboardHookStruct.vkCode, _
                                   MyKeyboardHookStruct.scanCode, _
                                   keyState, _
                                   inBuffer, _
                                   MyKeyboardHookStruct.flags) = 1 Then
                            Dim e As KeyPressEventArgs = New KeyPressEventArgs(System.Convert.ToChar(inBuffer(0)))
                            RaiseEvent KeyPress(Me, e)
                        End If
                    End If

                    If wParam = WM_KEYUP OrElse wParam = WM_SYSKEYUP Then
                        Dim keyData As Keys = CType(MyKeyboardHookStruct.vkCode, Keys)
                        Dim e As KeyEventArgs = New KeyEventArgs(keyData)
                        RaiseEvent KeyUp(Me, e)
                    End If
                End If
            End If

            Return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam)
        End Function

    End Class


    Public Class MouseHook
        Inherits BaseHook

        Public Event OnMouseActivity As MouseEventHandler

        Private hMouseHook As Integer = 0
        Private MouseHookProcedure As HookProc

        Private ThreadHook As Boolean = True


        Public Sub New()
            Call Start()
        End Sub

        Public Sub New(ByVal mThreadHook As Boolean)
            ThreadHook = mThreadHook
            Call Start()
        End Sub

        Protected Overrides Sub Finalize()
            Try
                [Stop]()
            Finally
                MyBase.Finalize()
            End Try
        End Sub

        Private Sub Start()
            MouseHookProcedure = New HookProc(AddressOf Me.MouseHookProc)
            If ThreadHook Then '线程钩子
                hMouseHook = SetWindowsHookEx(HookType.WH_MOUSE, _
                                                         MouseHookProcedure, _
                                                         GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), _
                                                         GetCurrentThreadId)
            Else '系统钩子
                hMouseHook = SetWindowsHookEx(HookType.WH_MOUSE_LL, _
                                                         MouseHookProcedure, _
                                                         GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), _
                                                         0)
            End If

            If (hMouseHook = 0) Then
                [Stop]()
                Throw New Exception("SetWindowsHookEx is failed in MouseHook.")
            End If
        End Sub

        Public Sub [Stop]()
            Dim retMouse As Boolean = True

            If hMouseHook <> 0 Then
                retMouse = UnhookWindowsHookEx(hMouseHook)
                hMouseHook = 0
            End If

            If Not retMouse Then Throw New Exception("UnhookWindowsHookEx failed in MouseHook.")
        End Sub

        Private Function MouseHookProc(ByVal nCode As Integer, ByVal wParam As Integer, ByVal lParam As IntPtr) As Integer
            If nCode >= 0 Then
                Dim button As MouseButtons = MouseButtons.None
                Select Case wParam
                    Case WM_LBUTTONDOWN
                        button = MouseButtons.Left
                    Case WM_RBUTTONDOWN
                        button = MouseButtons.Right
                    Case WM_MBUTTONDOWN
                        button = MouseButtons.Middle
                    Case WM_MOUSEWHEEL
                  

                End Select

                Dim clickCount As Integer = 0
                If Not button = MouseButtons.None Then
                    If wParam = WM_LBUTTONDBLCLK OrElse wParam = WM_RBUTTONDBLCLK Then
                        clickCount = 2
                    Else
                        clickCount = 1
                    End If
                End If

                Dim MyMouseHookStruct As MouseLLHookStruct
                MyMouseHookStruct = CType(Marshal.PtrToStructure(lParam, GetType(MouseLLHookStruct)), MouseLLHookStruct)
                Dim e As MouseEventArgs = New MouseEventArgs(button, _
                                                             clickCount, _
                                                             MyMouseHookStruct.pt.x, _
                                                             MyMouseHookStruct.pt.y, _
                                                             MyMouseHookStruct.mouseData)
                RaiseEvent OnMouseActivity(Me, e)
            End If
            Return CallNextHookEx(hMouseHook, nCode, wParam, lParam)
        End Function

    End Class



    ''' <summary>
    ''' 窗体类Hook
    ''' </summary>
    Public Class FormHook
        Inherits BaseHook

        Private hFormHook As Integer = 0
        Private FormHookProcedure As HookProc

        Public Sub New()
            Start()
            AddHandler Application.ApplicationExit, AddressOf Application_ApplicationExit
        End Sub

      

        Protected Overrides Sub Finalize()
            Try
                [Stop]()
            Finally
                MyBase.Finalize()
            End Try
        End Sub

        Private Sub Start()
            FormHookProcedure = New HookProc(AddressOf Me.FormHookProc)
            hFormHook = SetWindowsHookEx(HookType.WH_CALLWNDPROC, _
                                          FormHookProcedure, _
                                          GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), _
                                          GetCurrentThreadId)
            If (hFormHook = 0) Then
                [Stop]()
                Throw New Exception("SetWindowsHookEx is failed in FormHook.")
            End If
        End Sub

        Public Sub [Stop]()
            Dim retForm As Boolean = True

            If hFormHook <> 0 Then
                retForm = UnhookWindowsHookEx(hFormHook)
                hFormHook = 0
            End If

            If Not retForm Then Throw New Exception("UnhookWindowsHookEx failed in FormHook.")
        End Sub

        Private Function FormHookProc(ByVal nCode As Integer, ByVal wParam As Integer, ByVal lParam As IntPtr) As Integer
            If nCode = HC_ACTION Then
                Dim MyHookStruct As CWPSTRUCT
                MyHookStruct = CType(Marshal.PtrToStructure(lParam, GetType(CWPSTRUCT)), CWPSTRUCT)

                Select Case MyHookStruct.message
                    Case &H85, &H86 '绘制非客户区
                        Dim hdc As IntPtr = GetWindowDC(MyHookStruct.hwnd)
                        Dim g As Graphics = Graphics.FromHdc(hdc)
                        g.DrawString("Test-->Test-->Test-->Test", New Font("宋体", 20), Brushes.Blue, 0, -10)

                        ReleaseDC(MyHookStruct.hwnd, hdc) : g.Dispose()
                    Case Else
                        Return CallNextHookEx(hFormHook, nCode, wParam, lParam)
                End Select
                Return 1
            End If
            Return CallNextHookEx(hFormHook, nCode, wParam, lParam)
        End Function

        '窗体退出后，注销不完全。加这个。
        Private Sub Application_ApplicationExit(sender As Object, e As EventArgs)
            Dim retForm As Boolean = True
            If hFormHook <> 0 Then
                retForm = UnhookWindowsHookEx(hFormHook)
                hFormHook = 0
            End If
            [Stop]()
        End Sub


        Public Declare Auto Function GetWindowDC Lib "user32.dll" (ByVal hWnd As IntPtr) As IntPtr
        Public Declare Auto Function ReleaseDC Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal hdc As IntPtr) As IntPtr

       

    End Class



End Namespace


